Mapping Global Conflict

We will explore a data set from the Upsalla Data Conflict Program. The UDCP is one of the oldest and most robust datasets aggregating conflict events around the globe. To learn more about how the data is collected check out the University of Upsalla’s methodology page.

To start, we will download and clean the data. Then some light analysis and plotting. Finally, we will make a prediction using the insights we’ve gained.

Get Oriented

Here’s a plot to get us oriented.

# The World Map
world <- map_data('world')

# 
ggplot() +
    geom_map(data = world, map = world, aes(long, lat, map_id = region), 
             color = 'white', fill = 'gray50', alpha = .2) +
    geom_point(data = c_df, aes(longitude, latitude, color = as.factor(type_of_violence)), 
               alpha = .8) +
    theme(legend.title = element_text('Type of Violence')) +
    labs(title = 'Conflict Events in 2019') +
    scale_color_brewer(palette = 'Set1', labels = c('State Based', 'Non-State', 'One-Sided')) +
    guides(color = guide_legend('Type of Violence'))

Download Data and Prep

This is the nitty-gritty of getting the data and putting it into a usable form. An important step, but a tedious one to read through. Feel free to skip ahead.

setwd(x)

# get data
raw_list <- fromJSON('https://ucdpapi.pcr.uu.se/api/gedevents/20.1?pagesize=1000&StartDate=2019-01-01&EndDate=2019-12-31')

# extract results list
result <- raw_list$Result

# Null to NA function to apply through
null_to_na <- function(x) {
    
    for(i in 1:length(x))
        if(is.null(x[[i]])){
            x[[i]] <- NA
        } else {
            next
        }
    return(x)
    
}

# Set nulls to NA
result <- lapply(result, null_to_na)

# Initialize data frame with first element of results
c_df<- data.frame(result[[1]])

# Add the rest w/loop
for (i in 2:length(result)){
    c_df <- rbind(c_df, data.frame(result[[i]]))
}

# get next URL
URL <- prac$NextPageUrl
url_list <- c(URL, rep(NA, 50))

# Get list of URL's
for( i  in 2:39){
    listing <- fromJSON(URL)
    if (listing$NextPageUrl != ""){
        url_list[i] <- listing$NextPageUrl
        Sys.sleep(.2)
        URL <- listing$NextPageUrl
    } else {
        break
    }
}

# Manually subset to remove NA's
url_list <- url_list[!is.na(url_list)]

# This function should take a vector of URL's that return JSON, and
# give back data frames of data
get_all_data <- function(x) {
    # get Data
    data <- fromJSON(x)
    
    # subset Data
    data <- data$Result
    
    # Turn Nulls to NA's
    data <- lapply(data, null_to_na)
    
    # Initialize data frame with first element of results
    c_df<- data.frame(data[[1]])
    
    # Add the rest w/loop
    for (i in 2:length(data)){
        c_df <- rbind(c_df, data.frame(data[[i]]))
    }
    
    return(c_df)
}

# lappy over our URL with get_all_data
yes <- lapply(url_list, get_all_data)

# Collapse list of df's to single df
yes <- bind_rows(yes)

# Add page 1
c_df <- rbind(c_df, yes)

# As tibble
c_df <- c_df %>% 
    as_tibble(c_df)

# Create a civilian deaths categorical variable
c_df <- c_df %>% 
    mutate(civ_cat = case_when(deaths_civilians > 0 ~ 'yes', TRUE ~ 'no'),
           date = date(date_start),
           unk_cat = case_when(deaths_unknown > 0 ~ 'yes', TRUE ~ 'no'),
           word_count = str_count(source_article, ' ')) 


# Save file for later use
#save(c_df, file = "C:/Users/Car/Desktop/Messing Around/Exploration Mapping/mapping_conflict/data/conflict_19.Rds")

load(file = 'C:/Users/Car/Desktop/Messing Around/Exploration Mapping/mapping_conflict/data/conflict_19.Rds')

Explore


skimr::skim(c_df)

Tables

Random Subset

set.seed(42)
kable(c_df %>%
        filter(best > 10) %>% 
        sample_n(5) %>%
        select(date_start, country, side_a, side_b, deaths_a, deaths_b, civ_cat) %>% 
        arrange(date_start), col.names = c('Date', 'Conflict Location', 'Side A', 'Side B', 'Deaths A', 'Deaths B', 'Civilian Causualties'), align = 'c', caption = 'Random Subset')

Most Events By Country

kable(c_df %>%
        add_count(country) %>% 
        group_by(country) %>% 
        summarise(country = country, total_estimated = sum(best), n = n) %>%
        distinct() %>% 
        select(country, n, total_estimated) %>% 
        arrange(desc(n)) %>% 
        head(10), col.names = c('Conflict Location', 'Total Events', 'Total Deaths'), align = 'c', caption = 'Most Deaths')

Afghanistan had far and away the most conflicts, followed by Syria and Mexico.

Events with the Largest Death Counts

top_events <- kable(c_df %>% 
        select(date_start, country, side_a, side_b, deaths_a, deaths_b, deaths_unknown, best, civ_cat) %>%
        arrange(desc(best)) %>%
        head(10), 
        col.names = c('Date', 'Conflict Location', 'Side A', 'Side B', 'Deaths A', 'Deaths B', 'Unknown Deaths', 'Best Estimate for Total Deaths', 'Civilian Causualties'), 
        align = 'c', 
        caption = 'Largest Death Count per Conflict Event in 2019')

We notice a few things from the table above. Mexico had many deadly conflict events in 2019, their dates are truncated to the first of the month, and nearly all deaths are classified as ‘unknown’. This leads me to believe that the deaths are not one single conflict event, but instead a collection of smaller events that are aggregated and reported at the end of the month.

USA Involved


# USA involved
kable(c_df %>%
    filter(side_b_new_id == 769 | side_a_new_id == 769) %>% 
        select(date_start, country, side_a, side_b, deaths_a, deaths_b, civ_cat) %>% 
        arrange(date_start), col.names = c('Date', 'Conflict Location', 'Side A', 'Side B', 'Deaths A', 'Deaths B', 'Civilian Causualties'), align = 'c', caption = 'USA Directly Involved')

Doesn’t appear to be a ton of direct US involvement in the 19 year old conflict with the Taliban.


# USA involved
kable(c_df %>%
    filter(side_a_new_id == 130 | side_a_new_id == 130) %>% 
        select(date_start, country, side_a, side_b, deaths_a, deaths_b, civ_cat) %>% 
        arrange(desc(deaths_b)) %>% 
        head(10), col.names = c('Date', 'Conflict Location', 'Side A', 'Side B', 'Deaths A', 'Deaths B', 'Civilian Causualties'), align = 'c', caption = 'Afghanistan Military')

So it appears the US itself isn’t as active as the Afghanistan state military, however considering the US spends 38 billion (with a b) dollars in 2019 alone, well…

Civilian Deaths Globally


# How many?
kable(c_df %>% 
    count(civ_cat) %>% 
    arrange(n), caption = 'Events With Civilian Deaths', col.names = c('Civilian Deaths', 'Number of Events'))

# Table Civilian Involved Conflicts
c_df %>% 
    filter(side_b == 'Civilians') %>% 
    mutate(date = floor_date(as.Date(date_start), unit = 'days')) %>% 
    select(date, side_a, side_b, deaths_unknown, deaths_civilians, civ_cat) %>%
    arrange(desc(deaths_civilians)) %>% 
    head(10) %>% 
    ggplot() +
    geom_col(aes(x = factor(date), y = deaths_civilians, fill = side_a)) +
    labs(x = 'Event Date', y = 'Civilian Deaths', 
         title = 'Most Deaths in Events where Civilians Are Coded as Side B') +
    theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.title = element_text('Side A'))

# How many?
c_df %>% 
    filter(side_b == 'Civilians') %>% 
    mutate(side_a = abbreviate(side_a, minlength = 15)) %>% 
    select(deaths_civilians, deaths_unknown, civ_cat, side_a, side_b) %>% 
    count(side_a, sort = TRUE) %>% 
    arrange(desc(n)) %>% 
    head(10) %>% 
    ggplot(aes(x = reorder(side_a, -n), y = n, fill = side_a)) +
    geom_col() +
    labs(x = 'Side A', y = 'Number of Events in 2019', 
         title = 'Number of Civilian Coded Side B Events')+
    theme(axis.text.x = element_text(angle = 45, hjust = 1), 
          legend.position = 'none')

Average Deaths Per Conflict

Like we mention above, Mexico’s death counts per event are suspicious. There might be some aggregation going on.

Deaths per Conflict

They appear to be the same.


world <- map_data('world')

# Okay, kinda like, civilian deaths mapped globally
world_civ <- ggplot() +
    geom_map(data = world, map = world, aes(long, lat, map_id = region), 
             color = 'white', fill = 'gray50', alpha = .5) +
    geom_point(data = c_df, aes(longitude, latitude, 
                                color = as.factor(civ_cat), 
                                group = id), alpha = .7) +
    theme(axis.ticks = element_blank(),
          axis.text = element_blank(),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank()) +
    scale_color_manual(values = c('no' = 'darkblue', 'yes' = 'red'), 
                       labels = c('No', 'Yes')) +
    labs(title = 'Civilian Deaths in Conflict Events (2019)', x ='', y= '') +
    guides(color = guide_legend('Civilian Deaths', reverse = TRUE))

ggsave('world_civ.png', plot = world_civ, path = "C:/Users/Car/Desktop/Messing Around/Exploration Mapping/mapping_conflict/figures/", dpi = 300, units = 'in', width = 12, height = 8)
set.seed(48)
samp <- kable(c_df %>% 
    filter(best > 10) %>%
    sample_n(5) %>% 
    mutate(date_start = date(date_start)) %>%
    select(date_start, country, side_a, side_b, best, civ_cat, unk_cat), 
    col.names = c('Date', 'Country', 'Side A', 'Side B', 'Deaths', 'Civilian Deaths', 'Unknown Deaths'), align = 'c') %>% 
    column_spec(2:3, width = '5cm') %>% 
    column_spec(6:7, width = '3cm')

save_kable(samp, file = "figures/sample_row.png", self_contained = TRUE)

Lets look at January 2019 for a few select countries.


# Set up data frame
# January only, India only, change to dates, select specific columns
c_jan_19_in <- c_df %>% 
    filter(country == 'India') %>%
    select(id, best, latitude, longitude, side_a, side_b, date_start, date_end) %>% 
    mutate(date_start = as.Date(date_start), date_end = as.Date(date_end)) %>% 
    filter(date_start <= '2019-1-31' & date_start >= '2019-1-1') 

# Get shapefile -- https://hub.arcgis.com/datasets/2b37b84e67374fb98577c20ef8be6c62_0
india_sf <- read_sf('india')

# Beautiful, needed ids and frame for plotly. Frame needs to be in numeric or prob character
# maybe as.Date as.char
ind <- 
    ggplot(data = c_jan_19_in) +
    geom_sf(data = india_sf, fill = 'gray50', alpha =.1) +
    geom_point(data = c_jan_19_in, aes(longitude, latitude, ids = id,
                                       frame = as.character(date_start)),
               alpha = .8, color = 'darkred', size = 1) +
    labs(x = 'Longitude', 
         y = 'Latitude',
         title = 'Conflict in India January 2019')

# Plotly instead?
ind <- ggplotly(ind, width = 500, height = 500)

# Add opts
ind %>% 
    animation_opts(1000, easing = "linear", redraw = FALSE) 

Modeling

We will build a random forest model to predict whether or not a conflict had civilian deaths.

Then we will use the model to gauge if ‘unknown’ deaths were possibly mislabeled. In order to do this, we’re going to remove unknown deaths from the test/train set. Then check if the ‘unknown deaths’ data set has around the same accuracy as the test data set.

A discovery from our exploration that could affect us predicting civilian deaths, is that occasionally Side B is labeled as civilians. In order to keep the model from using side_b ‘Civilian’ labeling, we will not use ‘side_b’. For the same reasoning, we will not use ‘type of violence’ as the third type, ‘One Sided’ means action against civlians.


# Step one, filter out Unknowns, set up data frame. Becuase of a prediction we will
# be attempting later, we will remove Brazil and Mexico. 
no_unk_df <- c_df %>% 
    filter(deaths_unknown == 0) %>%
    filter(country != 'Mexico', country != 'Brazil') %>% 
    mutate(date = as.Date(date_start)) %>% 
    select(id, conflict_new_id, date, side_a,
           number_of_sources, latitude, longitude, country, region, 
    event_clarity, deaths_a, deaths_b, civ_cat) %>% 
    mutate_if(is.character, factor) %>% 
    mutate(conflict_new_id = factor(conflict_new_id))

Split data into test and training.


# Create training and testing sets
set.seed(715)
c_split <- initial_split(no_unk_df, strata = civ_cat)
c_train <- training(c_split)
c_test <- testing(c_split)

Prepare our data.


# set up our recipe using training data
c_rec <- recipe(civ_cat ~ ., data = c_train) %>% 
    # Make ID be an id
    update_role(id, new_role = 'id') %>% 
    # Reduce categories
    step_other(side_a, country, conflict_new_id, threshold = .02) %>% 
    # turn all categories into numbers
    step_dummy(all_nominal(), -all_outcomes()) %>% 
    # Use week
    step_date(date, features = 'week') %>% 
    # Rm complete date
    step_rm(date) %>% 
    # downsample the civilian deaths
    themis::step_downsample(civ_cat)

# prep our data
c_prep <- prep(c_rec)

# Take a look at new data
knitr::kable(juice(c_prep) %>% sample_n(5) %>% select(1:10), align = 'c', caption = 'Prepped for Modeling, A Few Columns')

Prepare our tuning specification. In this case we will tune the number of predictors to sample at each split, as well as the number of data points required to be in a node to be split further.


# Set our random forest model specification
tune_spec <- rand_forest(
    mtry = tune(),
    trees = 1000,
    min_n = tune()
    ) %>% 
    set_mode('classification') %>% 
    set_engine('ranger')

# Set our workflow
tune_wf <- workflow() %>% 
    add_recipe(c_rec) %>% 
    add_model(tune_spec)

# Train on our CV
start.time <- Sys.time()

set.seed(234)

c_folds <- vfold_cv(c_train)

doParallel::registerDoParallel()

# This takes time
tune_res <- tune_grid(
    tune_wf, 
    resamples = c_folds,
    grid = 20
)

# For timing
end.time <- Sys.time()
time.taken <- end.time - start.time
knitr::kable(round(time.taken), col.names = 'Time to Fit')

# Plot of CV Tuned Hyperparameters
tune_res %>% 
    collect_metrics() %>% 
    filter(.metric == 'roc_auc') %>% 
    pivot_longer(cols = c('min_n', 'mtry'), names_to = 'tuned',
                 values_to = 'tune_val') %>%
    ggplot(aes(tune_val, mean, color = tuned)) +
    geom_line(alpha = .8, size = 1.5) +
    geom_point() +
    scale_x_continuous(breaks = seq(10,40,2)) +
    facet_wrap(~tuned) +
    labs(x = 'Value of Tuned Hyperparameter', y = 'Avg Roc Auc') +
    theme(legend.position = 'none')

The plot above shows our Roc Area Under the Curve at different hyperparameters levels.

We pick the best hyperparameters from the tuning above and fit our final model.


# Select Best Specifications Based on Roc Auc
best_auc <- select_best(tune_res, 'roc_auc')

# Finalize model w/tuned Hyperparmeters 
final_rf <- finalize_model(
    tune_spec, 
    best_auc
)

Check the importance of our variables.


# Variable Importance Plots
library(vip)

# VIP Plot
final_rf %>% 
    set_engine('ranger', importance = 'permutation') %>% 
    fit(civ_cat~ .,
        data = juice(c_prep) %>% select(-id)) %>% 
    vip(geom = 'point') +
    labs(title = 'Variable Importance')

The deaths are the most important variables for predicting civilian casualties. Specifically the deaths of ‘side_b’. It’s hard to say exactly why. Is it because with higher deaths from side B there’s more likely to be civilian casualties? Or is it because civilian deaths can be lumped in with side_b, leading to ‘no’ civilian deaths but high side_b deaths? This is, of course, complete and uniformed conjecture.


# Final workflow, with recipe and final model. 
final_wf <- workflow() %>% 
    add_recipe(c_rec) %>% 
    add_model(final_rf)
# last_fit does a final fit on training data, then evaluates on testing data
final_res <- final_wf %>% 
    last_fit(c_split)

# Save testing accuracy for later
testing_accuracy <- final_res %>% 
    collect_metrics() %>% 
    filter(.metric == 'accuracy') %>% 
    select(.estimate)

# Look at our metrics
knitr::kable(final_res %>% 
    collect_metrics() %>% mutate(.estimate = round(.estimate, 3)), align ='c', caption = 'Metrics')

Not bad.

# Glance at predictions
knitr::kable(final_res %>% 
    collect_predictions() %>% sample_n(5), align ='c', caption = 'Predictions')

Well that built us a model. Now can we use it on the ‘unknown’ deaths to predict which ones might have had civilian casualties?

I wonder if the events with the unknowns are from the same distribution.


# Create groups for when there's unknown deaths, then plot. 
c_df %>% 
    mutate(un_cat = case_when(deaths_unknown > 0 ~ 'Yes',
                              TRUE ~ 'No')) %>% 
    filter(best < 75) %>% 
    ggplot(aes(best, fill = un_cat)) +
    geom_density(alpha = .5) +
    labs(x = 'Estimated Deaths', y = 'Number of Events',
         title = 'Events With Unknown Deaths') +
    scale_fill_discrete(name = 'Unkown Deaths')

From this first quick glance, they appear pretty close.

extrafont::loadfonts(device="win")

# Filtering by events greater than 75, and finding proportions of events
# with unknown deaths. 
c_df %>% 
    mutate(un_cat = case_when(deaths_unknown > 0 ~ 'Yes',
                              TRUE ~ 'No')) %>% 
    count(country, un_cat) %>% 
    group_by(country) %>% 
    mutate(proportion = (n / sum(n))) %>% 
    filter(n > 75) %>% 
    ggplot(aes(country, proportion, fill = un_cat)) +
    geom_col(position = 'stack') +
    theme(legend.title = element_text('Unkown Deaths'), 
           axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0)) +
    labs(title = 'Proportion of Events with Unknown Deaths vs Without') +
    scale_fill_discrete(name = 'Unknown Deaths')

Mexico, one of the counties with the most events, has a very large proportion of unknown deaths. Brazil as well. This leads me to conclude that it is NOT appropriate to use my model to predict civilian casualties on unknown deaths in Mexico or Brazil. Therefore, we will retrain our model excluding those two countries. Then predict unknown deaths vs civilian deaths on the rest.


# Get all data we pulled from earlier
unk <- c_df %>% 
    filter(deaths_unknown > 0) %>% 
    filter(country != 'Mexico', country != 'Brazil') %>% 
    mutate(date = as.Date(date_start)) %>% 
    select(id, conflict_new_id, date, side_a, 
    number_of_sources, latitude, longitude, country, region, 
    event_clarity, deaths_a, deaths_b, civ_cat) %>% 
    mutate_if(is.character, factor) %>% 
    mutate(conflict_new_id = factor(conflict_new_id))

Our predictable unknown data is reduced from 1550 to 611 when we remove Brazil and Mexico. Again, as a reminder, we’re assuming there’s no such ‘real’ category of unknown casualties: casualties either come from civilians or combatants. So, we’re using our model to predict civilian casualties on events with unknown deaths.

# Final fit used on training data
completed <- final_wf %>% 
    fit(c_train)

# Transform our new 'unknowns' data into same as model
unk_baked <- bake(prep(c_rec), unk)

# Pull out model
my_model <- completed %>% 
    pull_workflow_fit()

# Make predictions
predicted_unknowns <- predict(my_model, new_data = unk_baked) %>% 
    cbind(unk, .)

# Get column of unknown deaths
unk_deaths <- select(c_df, id, deaths_unknown)

# Add our Unknown deaths back, and keep only relevant data. 
pred_short <- predicted_unknowns %>% 
    left_join(x = ., y = unk_deaths, by = 'id') %>% 
    select(id, country, deaths_unknown, civ_cat, .pred_class)

knitr::kable(pred_short %>% sample_n(8), align = 'c', caption = 'Unknown Deaths, Civilian Deaths and Civ Death Predictions')

There appears to be some mismatch between the civilian deaths category and our predictions. Assuming our model retains it’s accuracy on our ‘new’ data, we postulate that there should be about a 90% accuracy rate.

# Get our accuracy on our new data
unknown_accuracy <- sum(pred_short$civ_cat == pred_short$.pred_class) / nrow(pred_short)

# Create dataframe for plot
compared_perc <- data.frame(acc = c('Unknown Accuracy', 'Testing Accuracy'), 
                  perc = c(scales::percent(unknown_accuracy), scales::percent(pull(testing_accuracy))))

# No perc
compared <- data.frame(acc = c('Unknown Accuracy', 'Testing Accuracy'), 
                  perc = c(unknown_accuracy, pull(testing_accuracy)))

# Plot
ggplot(compared) +
    geom_col(aes(x = acc, y = perc, fill = acc)) +
    labs(x = '', y = 'Percent Accurate', title = 'Accuracy of Our Two Sets',
         subtitle = 'Model Testing Set vs. Unknown Deaths Set') +
    theme(legend.position = 'none') +
    scale_y_continuous(limits = c(0, 1), labels = percent)

This tells me two things, of which either, or both, could be true.

One, using the model on the unknown subset made for highly inaccurate predictions on the civilian deaths category.

Or two, the ‘unknown’ deaths have an unusually high inaccurate labeling of civilian casualties.

knitr::kable(compared_perc, alight = 'c', caption = 'Accuracy of Model on Unknowns vs Testing Data')

Thank you, and feel free to contact me through github with any questions, errors, or comments.

-Carson Poe

This project was heavily inspired by David Robinson and Julia Silge. Thank you for the inspirational tutorials!

LS0tDQp0aXRsZTogIk1hcHBpbmcgQ29uZmxpY3QiDQphdXRob3I6ICJDYXJzb24gUG9lIg0KZGF0ZTogIjEyLzI4LzIwMjAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KDQpleHRyYWZvbnQ6OmxvYWRmb250cyhkZXZpY2U9IndpbiIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoaHR0cikNCmxpYnJhcnkoUkpTT05JTykNCmxpYnJhcnkocjJkMykNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KGdncGxvdDIpDQp0aGVtZV9zZXQodGhlbWVfbGlnaHQoKSkNCmxpYnJhcnkobWFwcykNCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShnZ2FuaW1hdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShrbml0cikgI25lZWQNCmxpYnJhcnkodGlkeW1vZGVscykNCmxpYnJhcnkoa2FibGVFeHRyYSkgIyBOZWVkDQoNCg0KYGBgDQoNCiMgTWFwcGluZyBHbG9iYWwgQ29uZmxpY3QNCg0KDQoNCldlIHdpbGwgZXhwbG9yZSBhIGRhdGEgc2V0IGZyb20gdGhlIFtVcHNhbGxhIERhdGEgQ29uZmxpY3QgUHJvZ3JhbV0oaHR0cHM6Ly91Y2RwLnV1LnNlLyMvKS4gDQpUaGUgVURDUCBpcyBvbmUgb2YgdGhlIG9sZGVzdCBhbmQgbW9zdCByb2J1c3QgZGF0YXNldHMgYWdncmVnYXRpbmcgY29uZmxpY3QgZXZlbnRzDQphcm91bmQgdGhlIGdsb2JlLiBUbyBsZWFybiBtb3JlIGFib3V0IGhvdyB0aGUgZGF0YSBpcyBjb2xsZWN0ZWQgY2hlY2sgb3V0IHRoZSANCltVbml2ZXJzaXR5IG9mIFVwc2FsbGEnc10oaHR0cHM6Ly93d3cucGNyLnV1LnNlL3Jlc2VhcmNoL3VjZHAvbWV0aG9kb2xvZ3kvKSBtZXRob2RvbG9neSBwYWdlLiANCg0KDQoNClRvIHN0YXJ0LCB3ZSB3aWxsIGRvd25sb2FkIGFuZCBjbGVhbiB0aGUgZGF0YS4gVGhlbiBzb21lIGxpZ2h0IGFuYWx5c2lzIGFuZCBwbG90dGluZy4gDQpGaW5hbGx5LCB3ZSB3aWxsIG1ha2UgYSBwcmVkaWN0aW9uIHVzaW5nIHRoZSBpbnNpZ2h0cyB3ZSd2ZSBnYWluZWQuIA0KDQoNCiMjIEdldCBPcmllbnRlZA0KDQoNCkhlcmUncyBhIHBsb3QgdG8gZ2V0IHVzIG9yaWVudGVkLiANCg0KDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQojIFRoZSBXb3JsZCBNYXANCndvcmxkIDwtIG1hcF9kYXRhKCd3b3JsZCcpDQoNCiMgDQpnZ3Bsb3QoKSArDQogICAgZ2VvbV9tYXAoZGF0YSA9IHdvcmxkLCBtYXAgPSB3b3JsZCwgYWVzKGxvbmcsIGxhdCwgbWFwX2lkID0gcmVnaW9uKSwgDQogICAgICAgICAgICAgY29sb3IgPSAnd2hpdGUnLCBmaWxsID0gJ2dyYXk1MCcsIGFscGhhID0gLjIpICsNCiAgICBnZW9tX3BvaW50KGRhdGEgPSBjX2RmLCBhZXMobG9uZ2l0dWRlLCBsYXRpdHVkZSwgY29sb3IgPSBhcy5mYWN0b3IodHlwZV9vZl92aW9sZW5jZSkpLCANCiAgICAgICAgICAgICAgIGFscGhhID0gLjgpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoJ1R5cGUgb2YgVmlvbGVuY2UnKSkgKw0KICAgIGxhYnModGl0bGUgPSAnQ29uZmxpY3QgRXZlbnRzIGluIDIwMTknKSArDQogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAnU2V0MScsIGxhYmVscyA9IGMoJ1N0YXRlIEJhc2VkJywgJ05vbi1TdGF0ZScsICdPbmUtU2lkZWQnKSkgKw0KICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZCgnVHlwZSBvZiBWaW9sZW5jZScpKQ0KDQpgYGANCg0KDQoNCg0KDQojIyBEb3dubG9hZCBEYXRhIGFuZCBQcmVwDQoNCg0KVGhpcyBpcyB0aGUgbml0dHktZ3JpdHR5IG9mIGdldHRpbmcgdGhlIGRhdGEgYW5kIHB1dHRpbmcgaXQgaW50byBhIHVzYWJsZSBmb3JtLiANCkFuIGltcG9ydGFudCBzdGVwLCBidXQgYSB0ZWRpb3VzIG9uZSB0byByZWFkIHRocm91Z2guIA0KRmVlbCBmcmVlIHRvIHNraXAgYWhlYWQuIA0KDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpzZXR3ZCh4KQ0KDQojIGdldCBkYXRhDQpyYXdfbGlzdCA8LSBmcm9tSlNPTignaHR0cHM6Ly91Y2RwYXBpLnBjci51dS5zZS9hcGkvZ2VkZXZlbnRzLzIwLjE/cGFnZXNpemU9MTAwMCZTdGFydERhdGU9MjAxOS0wMS0wMSZFbmREYXRlPTIwMTktMTItMzEnKQ0KDQojIGV4dHJhY3QgcmVzdWx0cyBsaXN0DQpyZXN1bHQgPC0gcmF3X2xpc3QkUmVzdWx0DQoNCiMgTnVsbCB0byBOQSBmdW5jdGlvbiB0byBhcHBseSB0aHJvdWdoDQpudWxsX3RvX25hIDwtIGZ1bmN0aW9uKHgpIHsNCiAgICANCiAgICBmb3IoaSBpbiAxOmxlbmd0aCh4KSkNCiAgICAgICAgaWYoaXMubnVsbCh4W1tpXV0pKXsNCiAgICAgICAgICAgIHhbW2ldXSA8LSBOQQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgbmV4dA0KICAgICAgICB9DQogICAgcmV0dXJuKHgpDQogICAgDQp9DQoNCiMgU2V0IG51bGxzIHRvIE5BDQpyZXN1bHQgPC0gbGFwcGx5KHJlc3VsdCwgbnVsbF90b19uYSkNCg0KIyBJbml0aWFsaXplIGRhdGEgZnJhbWUgd2l0aCBmaXJzdCBlbGVtZW50IG9mIHJlc3VsdHMNCmNfZGY8LSBkYXRhLmZyYW1lKHJlc3VsdFtbMV1dKQ0KDQojIEFkZCB0aGUgcmVzdCB3L2xvb3ANCmZvciAoaSBpbiAyOmxlbmd0aChyZXN1bHQpKXsNCiAgICBjX2RmIDwtIHJiaW5kKGNfZGYsIGRhdGEuZnJhbWUocmVzdWx0W1tpXV0pKQ0KfQ0KDQojIGdldCBuZXh0IFVSTA0KVVJMIDwtIHByYWMkTmV4dFBhZ2VVcmwNCnVybF9saXN0IDwtIGMoVVJMLCByZXAoTkEsIDUwKSkNCg0KIyBHZXQgbGlzdCBvZiBVUkwncw0KZm9yKCBpICBpbiAyOjM5KXsNCiAgICBsaXN0aW5nIDwtIGZyb21KU09OKFVSTCkNCiAgICBpZiAobGlzdGluZyROZXh0UGFnZVVybCAhPSAiIil7DQogICAgICAgIHVybF9saXN0W2ldIDwtIGxpc3RpbmckTmV4dFBhZ2VVcmwNCiAgICAgICAgU3lzLnNsZWVwKC4yKQ0KICAgICAgICBVUkwgPC0gbGlzdGluZyROZXh0UGFnZVVybA0KICAgIH0gZWxzZSB7DQogICAgICAgIGJyZWFrDQogICAgfQ0KfQ0KDQojIE1hbnVhbGx5IHN1YnNldCB0byByZW1vdmUgTkEncw0KdXJsX2xpc3QgPC0gdXJsX2xpc3RbIWlzLm5hKHVybF9saXN0KV0NCg0KIyBUaGlzIGZ1bmN0aW9uIHNob3VsZCB0YWtlIGEgdmVjdG9yIG9mIFVSTCdzIHRoYXQgcmV0dXJuIEpTT04sIGFuZA0KIyBnaXZlIGJhY2sgZGF0YSBmcmFtZXMgb2YgZGF0YQ0KZ2V0X2FsbF9kYXRhIDwtIGZ1bmN0aW9uKHgpIHsNCiAgICAjIGdldCBEYXRhDQogICAgZGF0YSA8LSBmcm9tSlNPTih4KQ0KICAgIA0KICAgICMgc3Vic2V0IERhdGENCiAgICBkYXRhIDwtIGRhdGEkUmVzdWx0DQogICAgDQogICAgIyBUdXJuIE51bGxzIHRvIE5BJ3MNCiAgICBkYXRhIDwtIGxhcHBseShkYXRhLCBudWxsX3RvX25hKQ0KICAgIA0KICAgICMgSW5pdGlhbGl6ZSBkYXRhIGZyYW1lIHdpdGggZmlyc3QgZWxlbWVudCBvZiByZXN1bHRzDQogICAgY19kZjwtIGRhdGEuZnJhbWUoZGF0YVtbMV1dKQ0KICAgIA0KICAgICMgQWRkIHRoZSByZXN0IHcvbG9vcA0KICAgIGZvciAoaSBpbiAyOmxlbmd0aChkYXRhKSl7DQogICAgICAgIGNfZGYgPC0gcmJpbmQoY19kZiwgZGF0YS5mcmFtZShkYXRhW1tpXV0pKQ0KICAgIH0NCiAgICANCiAgICByZXR1cm4oY19kZikNCn0NCg0KIyBsYXBweSBvdmVyIG91ciBVUkwgd2l0aCBnZXRfYWxsX2RhdGENCnllcyA8LSBsYXBwbHkodXJsX2xpc3QsIGdldF9hbGxfZGF0YSkNCg0KIyBDb2xsYXBzZSBsaXN0IG9mIGRmJ3MgdG8gc2luZ2xlIGRmDQp5ZXMgPC0gYmluZF9yb3dzKHllcykNCg0KIyBBZGQgcGFnZSAxDQpjX2RmIDwtIHJiaW5kKGNfZGYsIHllcykNCg0KIyBBcyB0aWJibGUNCmNfZGYgPC0gY19kZiAlPiUgDQogICAgYXNfdGliYmxlKGNfZGYpDQoNCiMgQ3JlYXRlIGEgY2l2aWxpYW4gZGVhdGhzIGNhdGVnb3JpY2FsIHZhcmlhYmxlDQpjX2RmIDwtIGNfZGYgJT4lIA0KICAgIG11dGF0ZShjaXZfY2F0ID0gY2FzZV93aGVuKGRlYXRoc19jaXZpbGlhbnMgPiAwIH4gJ3llcycsIFRSVUUgfiAnbm8nKSwNCiAgICAgICAgICAgZGF0ZSA9IGRhdGUoZGF0ZV9zdGFydCksDQogICAgICAgICAgIHVua19jYXQgPSBjYXNlX3doZW4oZGVhdGhzX3Vua25vd24gPiAwIH4gJ3llcycsIFRSVUUgfiAnbm8nKSwNCiAgICAgICAgICAgd29yZF9jb3VudCA9IHN0cl9jb3VudChzb3VyY2VfYXJ0aWNsZSwgJyAnKSkgDQoNCg0KIyBTYXZlIGZpbGUgZm9yIGxhdGVyIHVzZQ0KI3NhdmUoY19kZiwgZmlsZSA9ICJDOi9Vc2Vycy9DYXIvRGVza3RvcC9NZXNzaW5nIEFyb3VuZC9FeHBsb3JhdGlvbiBNYXBwaW5nL21hcHBpbmdfY29uZmxpY3QvZGF0YS9jb25mbGljdF8xOS5SZHMiKQ0KDQpsb2FkKGZpbGUgPSAnQzovVXNlcnMvQ2FyL0Rlc2t0b3AvTWVzc2luZyBBcm91bmQvRXhwbG9yYXRpb24gTWFwcGluZy9tYXBwaW5nX2NvbmZsaWN0L2RhdGEvY29uZmxpY3RfMTkuUmRzJykNCmBgYA0KDQoNCg0KDQoNCiMjIEV4cGxvcmUNCg0KYGBge3J9DQoNCnNraW1yOjpza2ltKGNfZGYpDQoNCg0KYGBgDQoNCg0KIyMjIFRhYmxlcw0KDQoNCg0KKipSYW5kb20gU3Vic2V0KioNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0Kc2V0LnNlZWQoNDIpDQprYWJsZShjX2RmICU+JQ0KICAgICAgICBmaWx0ZXIoYmVzdCA+IDEwKSAlPiUgDQogICAgICAgIHNhbXBsZV9uKDUpICU+JQ0KICAgICAgICBzZWxlY3QoZGF0ZV9zdGFydCwgY291bnRyeSwgc2lkZV9hLCBzaWRlX2IsIGRlYXRoc19hLCBkZWF0aHNfYiwgY2l2X2NhdCkgJT4lIA0KICAgICAgICBhcnJhbmdlKGRhdGVfc3RhcnQpLCBjb2wubmFtZXMgPSBjKCdEYXRlJywgJ0NvbmZsaWN0IExvY2F0aW9uJywgJ1NpZGUgQScsICdTaWRlIEInLCAnRGVhdGhzIEEnLCAnRGVhdGhzIEInLCAnQ2l2aWxpYW4gQ2F1c3VhbHRpZXMnKSwgYWxpZ24gPSAnYycsIGNhcHRpb24gPSAnUmFuZG9tIFN1YnNldCcpDQoNCmBgYA0KDQoNCg0KDQoqKk1vc3QgRXZlbnRzIEJ5IENvdW50cnkqKg0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0Ka2FibGUoY19kZiAlPiUNCiAgICAgICAgYWRkX2NvdW50KGNvdW50cnkpICU+JSANCiAgICAgICAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIA0KICAgICAgICBzdW1tYXJpc2UoY291bnRyeSA9IGNvdW50cnksIHRvdGFsX2VzdGltYXRlZCA9IHN1bShiZXN0KSwgbiA9IG4pICU+JQ0KICAgICAgICBkaXN0aW5jdCgpICU+JSANCiAgICAgICAgc2VsZWN0KGNvdW50cnksIG4sIHRvdGFsX2VzdGltYXRlZCkgJT4lIA0KICAgICAgICBhcnJhbmdlKGRlc2MobikpICU+JSANCiAgICAgICAgaGVhZCgxMCksIGNvbC5uYW1lcyA9IGMoJ0NvbmZsaWN0IExvY2F0aW9uJywgJ1RvdGFsIEV2ZW50cycsICdUb3RhbCBEZWF0aHMnKSwgYWxpZ24gPSAnYycsIGNhcHRpb24gPSAnTW9zdCBEZWF0aHMnKQ0KYGBgDQoNCg0KQWZnaGFuaXN0YW4gaGFkIGZhciBhbmQgYXdheSB0aGUgbW9zdCBjb25mbGljdHMsIGZvbGxvd2VkIGJ5IFN5cmlhIGFuZCBNZXhpY28uIA0KDQoNCg0KKipFdmVudHMgd2l0aCB0aGUgTGFyZ2VzdCBEZWF0aCBDb3VudHMqKg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQp0b3BfZXZlbnRzIDwtIGthYmxlKGNfZGYgJT4lIA0KICAgICAgICBzZWxlY3QoZGF0ZV9zdGFydCwgY291bnRyeSwgc2lkZV9hLCBzaWRlX2IsIGRlYXRoc19hLCBkZWF0aHNfYiwgZGVhdGhzX3Vua25vd24sIGJlc3QsIGNpdl9jYXQpICU+JQ0KICAgICAgICBhcnJhbmdlKGRlc2MoYmVzdCkpICU+JQ0KICAgICAgICBoZWFkKDEwKSwgDQogICAgICAgIGNvbC5uYW1lcyA9IGMoJ0RhdGUnLCAnQ29uZmxpY3QgTG9jYXRpb24nLCAnU2lkZSBBJywgJ1NpZGUgQicsICdEZWF0aHMgQScsICdEZWF0aHMgQicsICdVbmtub3duIERlYXRocycsICdCZXN0IEVzdGltYXRlIGZvciBUb3RhbCBEZWF0aHMnLCAnQ2l2aWxpYW4gQ2F1c3VhbHRpZXMnKSwgDQogICAgICAgIGFsaWduID0gJ2MnLCANCiAgICAgICAgY2FwdGlvbiA9ICdMYXJnZXN0IERlYXRoIENvdW50IHBlciBDb25mbGljdCBFdmVudCBpbiAyMDE5JykNCg0KDQpgYGANCg0KDQpXZSBub3RpY2UgYSBmZXcgdGhpbmdzIGZyb20gdGhlIHRhYmxlIGFib3ZlLiBNZXhpY28gaGFkIG1hbnkgZGVhZGx5IGNvbmZsaWN0IGV2ZW50cw0KaW4gMjAxOSwgdGhlaXIgZGF0ZXMgYXJlIHRydW5jYXRlZCB0byB0aGUgZmlyc3Qgb2YgdGhlIG1vbnRoLCBhbmQgbmVhcmx5IGFsbCBkZWF0aHMgYXJlDQpjbGFzc2lmaWVkIGFzICd1bmtub3duJy4gVGhpcyBsZWFkcyBtZSB0byBiZWxpZXZlIHRoYXQgdGhlIGRlYXRocyBhcmUgbm90IG9uZSBzaW5nbGUNCmNvbmZsaWN0IGV2ZW50LCBidXQgaW5zdGVhZCBhIGNvbGxlY3Rpb24gb2Ygc21hbGxlciBldmVudHMgdGhhdCBhcmUgYWdncmVnYXRlZA0KYW5kIHJlcG9ydGVkIGF0IHRoZSBlbmQgb2YgdGhlIG1vbnRoLiANCg0KDQoNCioqVVNBIEludm9sdmVkKioNCg0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KDQojIFVTQSBpbnZvbHZlZA0Ka2FibGUoY19kZiAlPiUNCiAgICBmaWx0ZXIoc2lkZV9iX25ld19pZCA9PSA3NjkgfCBzaWRlX2FfbmV3X2lkID09IDc2OSkgJT4lIA0KICAgICAgICBzZWxlY3QoZGF0ZV9zdGFydCwgY291bnRyeSwgc2lkZV9hLCBzaWRlX2IsIGRlYXRoc19hLCBkZWF0aHNfYiwgY2l2X2NhdCkgJT4lIA0KICAgICAgICBhcnJhbmdlKGRhdGVfc3RhcnQpLCBjb2wubmFtZXMgPSBjKCdEYXRlJywgJ0NvbmZsaWN0IExvY2F0aW9uJywgJ1NpZGUgQScsICdTaWRlIEInLCAnRGVhdGhzIEEnLCAnRGVhdGhzIEInLCAnQ2l2aWxpYW4gQ2F1c3VhbHRpZXMnKSwgYWxpZ24gPSAnYycsIGNhcHRpb24gPSAnVVNBIERpcmVjdGx5IEludm9sdmVkJykNCg0KYGBgDQoNCg0KDQpEb2Vzbid0IGFwcGVhciB0byBiZSBhIHRvbiBvZiBkaXJlY3QgVVMgaW52b2x2ZW1lbnQgaW4gdGhlIDE5IHllYXIgb2xkIGNvbmZsaWN0IHdpdGggdGhlIFRhbGliYW4uIA0KDQoNCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCg0KIyBVU0EgaW52b2x2ZWQNCmthYmxlKGNfZGYgJT4lDQogICAgZmlsdGVyKHNpZGVfYV9uZXdfaWQgPT0gMTMwIHwgc2lkZV9hX25ld19pZCA9PSAxMzApICU+JSANCiAgICAgICAgc2VsZWN0KGRhdGVfc3RhcnQsIGNvdW50cnksIHNpZGVfYSwgc2lkZV9iLCBkZWF0aHNfYSwgZGVhdGhzX2IsIGNpdl9jYXQpICU+JSANCiAgICAgICAgYXJyYW5nZShkZXNjKGRlYXRoc19iKSkgJT4lIA0KICAgICAgICBoZWFkKDEwKSwgY29sLm5hbWVzID0gYygnRGF0ZScsICdDb25mbGljdCBMb2NhdGlvbicsICdTaWRlIEEnLCAnU2lkZSBCJywgJ0RlYXRocyBBJywgJ0RlYXRocyBCJywgJ0NpdmlsaWFuIENhdXN1YWx0aWVzJyksIGFsaWduID0gJ2MnLCBjYXB0aW9uID0gJ0FmZ2hhbmlzdGFuIE1pbGl0YXJ5JykNCg0KDQpgYGANCg0KDQoNCg0KU28gaXQgYXBwZWFycyB0aGUgVVMgaXRzZWxmIGlzbid0IGFzIGFjdGl2ZSBhcyB0aGUgQWZnaGFuaXN0YW4gc3RhdGUgbWlsaXRhcnksIA0KaG93ZXZlciBjb25zaWRlcmluZyB0aGUgVVMgc3BlbmRzIFszOCBiaWxsaW9uXShodHRwczovL2NvbXB0cm9sbGVyLmRlZmVuc2UuZ292L1BvcnRhbHMvNDUvZG9jdW1lbnRzL1NlY3Rpb24xMDkwUmVwb3J0cy9TZWN0aW9uXzEwOTBfRlkxN19OREFBX0Nvc3Rfb2ZfV2Fyc190b19QZXJfVGF4cGF5ZXItRmVicnVhcnlfMjAyMC5wZGYpICh3aXRoIGEgYikgZG9sbGFycyBpbiAyMDE5IGFsb25lLCB3ZWxsLi4uDQoNCg0KDQoNCioqQ2l2aWxpYW4gRGVhdGhzIEdsb2JhbGx5KioNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCiMgSG93IG1hbnk/DQprYWJsZShjX2RmICU+JSANCiAgICBjb3VudChjaXZfY2F0KSAlPiUgDQogICAgYXJyYW5nZShuKSwgY2FwdGlvbiA9ICdFdmVudHMgV2l0aCBDaXZpbGlhbiBEZWF0aHMnLCBjb2wubmFtZXMgPSBjKCdDaXZpbGlhbiBEZWF0aHMnLCAnTnVtYmVyIG9mIEV2ZW50cycpKQ0KDQoNCmBgYA0KDQoNCg0KDQoNCmBgYHtyfQ0KDQojIFRhYmxlIENpdmlsaWFuIEludm9sdmVkIENvbmZsaWN0cw0KY19kZiAlPiUgDQogICAgZmlsdGVyKHNpZGVfYiA9PSAnQ2l2aWxpYW5zJykgJT4lIA0KICAgIG11dGF0ZShkYXRlID0gZmxvb3JfZGF0ZShhcy5EYXRlKGRhdGVfc3RhcnQpLCB1bml0ID0gJ2RheXMnKSkgJT4lIA0KICAgIHNlbGVjdChkYXRlLCBzaWRlX2EsIHNpZGVfYiwgZGVhdGhzX3Vua25vd24sIGRlYXRoc19jaXZpbGlhbnMsIGNpdl9jYXQpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhkZWF0aHNfY2l2aWxpYW5zKSkgJT4lIA0KICAgIGhlYWQoMTApICU+JSANCiAgICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9jb2woYWVzKHggPSBmYWN0b3IoZGF0ZSksIHkgPSBkZWF0aHNfY2l2aWxpYW5zLCBmaWxsID0gc2lkZV9hKSkgKw0KICAgIGxhYnMoeCA9ICdFdmVudCBEYXRlJywgeSA9ICdDaXZpbGlhbiBEZWF0aHMnLCANCiAgICAgICAgIHRpdGxlID0gJ01vc3QgRGVhdGhzIGluIEV2ZW50cyB3aGVyZSBDaXZpbGlhbnMgQXJlIENvZGVkIGFzIFNpZGUgQicpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoJ1NpZGUgQScpKQ0KDQoNCmBgYA0KDQoNCg0KDQoNCmBgYHtyfQ0KDQojIEhvdyBtYW55Pw0KY19kZiAlPiUgDQogICAgZmlsdGVyKHNpZGVfYiA9PSAnQ2l2aWxpYW5zJykgJT4lIA0KICAgIG11dGF0ZShzaWRlX2EgPSBhYmJyZXZpYXRlKHNpZGVfYSwgbWlubGVuZ3RoID0gMTUpKSAlPiUgDQogICAgc2VsZWN0KGRlYXRoc19jaXZpbGlhbnMsIGRlYXRoc191bmtub3duLCBjaXZfY2F0LCBzaWRlX2EsIHNpZGVfYikgJT4lIA0KICAgIGNvdW50KHNpZGVfYSwgc29ydCA9IFRSVUUpICU+JSANCiAgICBhcnJhbmdlKGRlc2MobikpICU+JSANCiAgICBoZWFkKDEwKSAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihzaWRlX2EsIC1uKSwgeSA9IG4sIGZpbGwgPSBzaWRlX2EpKSArDQogICAgZ2VvbV9jb2woKSArDQogICAgbGFicyh4ID0gJ1NpZGUgQScsIHkgPSAnTnVtYmVyIG9mIEV2ZW50cyBpbiAyMDE5JywgDQogICAgICAgICB0aXRsZSA9ICdOdW1iZXIgb2YgQ2l2aWxpYW4gQ29kZWQgU2lkZSBCIEV2ZW50cycpKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksIA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykNCg0KYGBgDQoNCg0KDQoNCkF2ZXJhZ2UgRGVhdGhzIFBlciBDb25mbGljdA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCg0KIyBPa2F5LCBsb3ZpbmcgdGhpcyBwbG90LCBncm91cCBieSBjb3VudHJ5IGFuZCByb3VuZGVkIGRhdGVzLCBzdW0gZGVhdGhzLCBwbG90DQojIEZpbHRlcmluZyBieSBtb3JlIHRoYW4gMzAwIGV2ZW50cyBoZXJlLg0KY19kZiAlPiUgDQogICAgYWRkX2NvdW50KGNvdW50cnkpICU+JSANCiAgICBmaWx0ZXIobiA+IDMwMCkgJT4lIA0KICAgIG11dGF0ZShyb3VuZGVkX2RhdGUgPSBmbG9vcl9kYXRlKGFzLkRhdGUoZGF0ZV9zdGFydCksIHVuaXQgPSAnbW9udGgnKSkgJT4lIA0KICAgIGdyb3VwX2J5KGNvdW50cnksIHJvdW5kZWRfZGF0ZSkgJT4lIA0KICAgIG11dGF0ZShzdW1fZGVhdGhzID0gc3VtKGJlc3QpKSAlPiUgDQogICAgc2VsZWN0KGNvdW50cnksIHN1bV9kZWF0aHMsIHJvdW5kZWRfZGF0ZSkgJT4lIA0KICAgIGdncGxvdChhZXMocm91bmRlZF9kYXRlLCBzdW1fZGVhdGhzLCBjb2xvciA9IGNvdW50cnkpKSArDQogICAgZ2VvbV9saW5lKCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gLjUpLCBsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsNCiAgICBsYWJzKHggPSAnRGF0ZXMnLCB5ID0gJ1RvdGFsIERlYXRocycsIHRpdGxlID0gJ0NvbmZsaWN0IERlYXRocyBQZXIgTW9udGggRm9yIENvdW50cmllcyB3aXRoIEdyZWF0ZXIgdGhlbiAzMDAgRXZlbnRzJykgKw0KICAgIGZhY2V0X3dyYXAofmNvdW50cnksIHNjYWxlcyA9ICdmcmVlX3knKQ0KDQpnZ3NhdmUoKQ0KDQpgYGANCg0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KIyBPa2F5LCByZWFsbHkgY29vbCBwbG90LiBEb2Vzbid0IHNob3cgc3QgZGV2LCBidXQgc2hvd3MgYXZlcmFnZSBkZWF0aHMsIHBsdXMgc2FtcGxlDQojIHNpemUsIG9yZGVyZWQgYnkgbW9zdA0KY19kZiAlPiUgDQogICAgc2VsZWN0KGNvdW50cnksIGJlc3QsIGRhdGVfc3RhcnQsIHJlZ2lvbiwgYmVzdCkgJT4lIA0KICAgIGZpbHRlcihiZXN0IDwgNzAwKSAlPiUgDQogICAgYWRkX2NvdW50KGNvdW50cnkpICU+JSANCiAgICBmaWx0ZXIobiA+IDEwMCkgJT4lIA0KICAgIGdyb3VwX2J5KGNvdW50cnkpICU+JSANCiAgICBtdXRhdGUoYXZlcmFnZSA9IChzdW0oYmVzdCkvbiksIHN1bV9kZWF0aHMgPSBzdW0oYmVzdCkpICU+JSANCiAgICBzZWxlY3QoLWJlc3QsIC1kYXRlX3N0YXJ0KSAlPiUgDQogICAgIyBTZWVtcyBsaWtlIGEgY2hlYXRlciB3YXkgdG8gbGltaXQgbnVtYmVyIG9mIHJvd3MNCiAgICBzdW1tYXJpc2UocmVnaW9uID0gdW5pcXVlKHJlZ2lvbiksDQogICAgICAgICAgbiA9IG1heChuKSwgDQogICAgICAgICAgYXZlcmFnZSA9IG1heChhdmVyYWdlKSwNCiAgICAgICAgICBzdW1fZGVhdGhzID0gbWF4KHN1bV9kZWF0aHMpKSAlPiUgDQogICAgbXV0YXRlKGNvdW50cnkgPSBmY3RfcmVvcmRlcihjb3VudHJ5LCAtYXZlcmFnZSkpICU+JSANCiAgICBnZ3Bsb3QoYWVzKGNvdW50cnksIGF2ZXJhZ2UsIGZpbGwgPSBjb3VudHJ5KSkrDQogICAgZ2VvbV9jb2woKSArDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMCgnbj0gJywgbiksIGFuZ2xlID0gOTApLCBoanVzdCA9ICd0b3AnKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgbGVnZW5kLnBvc2l0aW9uID0gICdub25lJykgKw0KICAgIGxhYnMoeCA9ICdDb3VudHJ5JywgdGl0bGUgPSAnQXZlcmFnZSBEZWF0aHMgcGVyIFZpb2xlbnQgRW5jb3VudGVyIDIwMTknLCB5ID0gJ0F2ZXJhZ2UgRGVhdGhzJykgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhuLmJyZWFrcyA9IDgpDQpgYGANCg0KTGlrZSB3ZSBtZW50aW9uIGFib3ZlLCBNZXhpY28ncyBkZWF0aCBjb3VudHMgcGVyIGV2ZW50IGFyZSBzdXNwaWNpb3VzLiBUaGVyZQ0KbWlnaHQgYmUgc29tZSBhZ2dyZWdhdGlvbiBnb2luZyBvbi4gDQoNCioqRGVhdGhzIHBlciBDb25mbGljdCoqDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCg0KIyBUaGlzIGlzIGJlYXV0aWZ1bC4gRWFzeSBnZ3Bsb3QgZm9yIGhpc3RvZ3JhbSBvZiBkZWF0aHMsIHcvIFJlZ2lvbiBGYWNldA0KY19kZiAlPiUNCiAgICBmaWx0ZXIoYmVzdCA8IDUwKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKHggPSBiZXN0KSkgKw0KICAgIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCwgYWVzKGZpbGw9cmVnaW9uKSkgKyANCiAgICBmYWNldF93cmFwKH5yZWdpb24pICsNCiAgICB0aGVtZV9idygpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsNCiAgICB4bGFiKCdEZWF0aHMgQmVzdCBFc3RpbWF0ZScpICsNCiAgICBsYWJzKHRpdGxlID0gJ0FyZSBzb21lIHJlZ2lvbnMgZXZlbnRzIG1vcmUgZGVhZGx5PycsIHkgPSdOdW1iZXIgb2YgRXZlbnRzJywgeD0nTnVtYmVyIG9mIERlYXRocycpIA0KICAgIA0KDQpgYGANCg0KDQoNClRoZXkgYXBwZWFyIHRvIGJlIHRoZSBzYW1lLg0KDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCndvcmxkIDwtIG1hcF9kYXRhKCd3b3JsZCcpDQoNCiMgT2theSwga2luZGEgbGlrZSwgY2l2aWxpYW4gZGVhdGhzIG1hcHBlZCBnbG9iYWxseQ0Kd29ybGRfY2l2IDwtIGdncGxvdCgpICsNCiAgICBnZW9tX21hcChkYXRhID0gd29ybGQsIG1hcCA9IHdvcmxkLCBhZXMobG9uZywgbGF0LCBtYXBfaWQgPSByZWdpb24pLCANCiAgICAgICAgICAgICBjb2xvciA9ICd3aGl0ZScsIGZpbGwgPSAnZ3JheTUwJywgYWxwaGEgPSAuNSkgKw0KICAgIGdlb21fcG9pbnQoZGF0YSA9IGNfZGYsIGFlcyhsb25naXR1ZGUsIGxhdGl0dWRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBhcy5mYWN0b3IoY2l2X2NhdCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGlkKSwgYWxwaGEgPSAuNykgKw0KICAgIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdubycgPSAnZGFya2JsdWUnLCAneWVzJyA9ICdyZWQnKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ05vJywgJ1llcycpKSArDQogICAgbGFicyh0aXRsZSA9ICdDaXZpbGlhbiBEZWF0aHMgaW4gQ29uZmxpY3QgRXZlbnRzICgyMDE5KScsIHggPScnLCB5PSAnJykgKw0KICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZCgnQ2l2aWxpYW4gRGVhdGhzJywgcmV2ZXJzZSA9IFRSVUUpKQ0KDQpnZ3NhdmUoJ3dvcmxkX2Npdi5wbmcnLCBwbG90ID0gd29ybGRfY2l2LCBwYXRoID0gIkM6L1VzZXJzL0Nhci9EZXNrdG9wL01lc3NpbmcgQXJvdW5kL0V4cGxvcmF0aW9uIE1hcHBpbmcvbWFwcGluZ19jb25mbGljdC9maWd1cmVzLyIsIGRwaSA9IDMwMCwgdW5pdHMgPSAnaW4nLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4KQ0KDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCg0OCkNCnNhbXAgPC0ga2FibGUoY19kZiAlPiUgDQogICAgZmlsdGVyKGJlc3QgPiAxMCkgJT4lDQogICAgc2FtcGxlX24oNSkgJT4lIA0KICAgIG11dGF0ZShkYXRlX3N0YXJ0ID0gZGF0ZShkYXRlX3N0YXJ0KSkgJT4lDQogICAgc2VsZWN0KGRhdGVfc3RhcnQsIGNvdW50cnksIHNpZGVfYSwgc2lkZV9iLCBiZXN0LCBjaXZfY2F0LCB1bmtfY2F0KSwgDQogICAgY29sLm5hbWVzID0gYygnRGF0ZScsICdDb3VudHJ5JywgJ1NpZGUgQScsICdTaWRlIEInLCAnRGVhdGhzJywgJ0NpdmlsaWFuIERlYXRocycsICdVbmtub3duIERlYXRocycpLCBhbGlnbiA9ICdjJykgJT4lIA0KICAgIGNvbHVtbl9zcGVjKDI6Mywgd2lkdGggPSAnNWNtJykgJT4lIA0KICAgIGNvbHVtbl9zcGVjKDY6Nywgd2lkdGggPSAnM2NtJykNCg0Kc2F2ZV9rYWJsZShzYW1wLCBmaWxlID0gImZpZ3VyZXMvc2FtcGxlX3Jvdy5wbmciLCBzZWxmX2NvbnRhaW5lZCA9IFRSVUUpDQoNCmBgYA0KDQoNCg0KTGV0cyBsb29rIGF0IEphbnVhcnkgMjAxOSBmb3IgYSBmZXcgc2VsZWN0IGNvdW50cmllcy4gDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KIyBTZXQgdXAgZGF0YSBmcmFtZQ0KIyBKYW51YXJ5IG9ubHksIEFmZ2hhbmlzdGFuIG9ubHksIGNoYW5nZSB0byBkYXRlcywgc2VsZWN0IHNwZWNpZmljIGNvbHVtbnMNCmNfamFuXzE5X2FmIDwtIGNfZGYgJT4lIA0KICAgIGZpbHRlcihjb3VudHJ5ID09ICdBZmdoYW5pc3RhbicpICU+JQ0KICAgIHNlbGVjdChpZCwgYmVzdCwgbGF0aXR1ZGUsIGxvbmdpdHVkZSwgc2lkZV9hLCBzaWRlX2IsIGRhdGVfc3RhcnQsIGRhdGVfZW5kKSAlPiUgDQogICAgbXV0YXRlKGRhdGVfc3RhcnQgPSBhcy5EYXRlKGRhdGVfc3RhcnQpLCBkYXRlX2VuZCA9IGFzLkRhdGUoZGF0ZV9lbmQpKSAlPiUgDQogICAgZmlsdGVyKGRhdGVfc3RhcnQgPD0gJzIwMTktMS0zMScgJiBkYXRlX3N0YXJ0ID49ICcyMDE5LTEtMScpIA0KDQojIEdldCBzaGFwZWZpbGUgLS0gaHR0cHM6Ly9odWIuYXJjZ2lzLmNvbS9kYXRhc2V0cy8yYjYzNTI3ODcwZWY0MTZiYWNmODNiY2FmMzg4Njg1Zl8wL2RhdGENCmFmZ19zZiA8LSByZWFkX3NmKCdhZmdoYW5pc3RhbicpDQoNCg0KIyBCZWF1dGlmdWwsIG5lZWRlZCBpZHMgYW5kIGZyYW1lIGZvciBwbG90bHkuIEZyYW1lIG5lZWRzIHRvIGJlIGluIG51bWVyaWMgb3IgcHJvYiBjaGFyYWN0ZXINCiMgbWF5YmUgYXMuRGF0ZSBhcy5jaGFyDQphZmcgPC0gDQogICAgZ2dwbG90KGRhdGEgPSBjX2phbl8xOV9hZikgKw0KICAgIGdlb21fc2YoZGF0YSA9IGFmZ19zZiwgZmlsbCA9ICdncmF5NTAnLCBhbHBoYSA9LjEpICsNCiAgICBnZW9tX3BvaW50KGRhdGEgPSBjX2phbl8xOV9hZiwgYWVzKGxvbmdpdHVkZSwgbGF0aXR1ZGUsIGlkcyA9IGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJhbWUgPSBhcy5jaGFyYWN0ZXIoZGF0ZV9zdGFydCkpLA0KICAgICAgICAgICAgICAgYWxwaGEgPSAuOCwgY29sb3IgPSAnZGFya3JlZCcsIHNpemUgPSAxKSArDQogICAgbGFicyh4ID0gJ0xvbmdpdHVkZScsIA0KICAgICAgICAgeSA9ICdMYXRpdHVkZScsDQogICAgICAgICB0aXRsZSA9ICdDb25mbGljdCBpbiBBZmdoYW5pc3RhbiBKYW51YXJ5IDIwMTknKQ0KDQojIFBsb3RseSBpbnN0ZWFkPw0KYWZnIDwtIGdncGxvdGx5KGFmZywgd2lkdGggPSA1MDAsIGhlaWdodCA9IDUwMCkNCg0KIyBOZWVkIHRvIHJlLXJ1biB0byBzZWUgaWYgdGhpcyBmaXhlcyBzaXppbmcgLSANCmFmZyAlPiUgDQogICAgYW5pbWF0aW9uX29wdHMoMTAwMCwgZWFzaW5nID0gImxpbmVhciIsIHJlZHJhdyA9IEZBTFNFKSANCg0KYGBgDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCiMgU2V0IHVwIGRhdGEgZnJhbWUNCiMgSmFudWFyeSBvbmx5LCBJbmRpYSBvbmx5LCBjaGFuZ2UgdG8gZGF0ZXMsIHNlbGVjdCBzcGVjaWZpYyBjb2x1bW5zDQpjX2phbl8xOV9pbiA8LSBjX2RmICU+JSANCiAgICBmaWx0ZXIoY291bnRyeSA9PSAnSW5kaWEnKSAlPiUNCiAgICBzZWxlY3QoaWQsIGJlc3QsIGxhdGl0dWRlLCBsb25naXR1ZGUsIHNpZGVfYSwgc2lkZV9iLCBkYXRlX3N0YXJ0LCBkYXRlX2VuZCkgJT4lIA0KICAgIG11dGF0ZShkYXRlX3N0YXJ0ID0gYXMuRGF0ZShkYXRlX3N0YXJ0KSwgZGF0ZV9lbmQgPSBhcy5EYXRlKGRhdGVfZW5kKSkgJT4lIA0KICAgIGZpbHRlcihkYXRlX3N0YXJ0IDw9ICcyMDE5LTEtMzEnICYgZGF0ZV9zdGFydCA+PSAnMjAxOS0xLTEnKSANCg0KIyBHZXQgc2hhcGVmaWxlIC0tIGh0dHBzOi8vaHViLmFyY2dpcy5jb20vZGF0YXNldHMvMmIzN2I4NGU2NzM3NGZiOTg1NzdjMjBlZjhiZTZjNjJfMA0KaW5kaWFfc2YgPC0gcmVhZF9zZignaW5kaWEnKQ0KDQojIEJlYXV0aWZ1bCwgbmVlZGVkIGlkcyBhbmQgZnJhbWUgZm9yIHBsb3RseS4gRnJhbWUgbmVlZHMgdG8gYmUgaW4gbnVtZXJpYyBvciBwcm9iIGNoYXJhY3Rlcg0KIyBtYXliZSBhcy5EYXRlIGFzLmNoYXINCmluZCA8LSANCiAgICBnZ3Bsb3QoZGF0YSA9IGNfamFuXzE5X2luKSArDQogICAgZ2VvbV9zZihkYXRhID0gaW5kaWFfc2YsIGZpbGwgPSAnZ3JheTUwJywgYWxwaGEgPS4xKSArDQogICAgZ2VvbV9wb2ludChkYXRhID0gY19qYW5fMTlfaW4sIGFlcyhsb25naXR1ZGUsIGxhdGl0dWRlLCBpZHMgPSBpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyYW1lID0gYXMuY2hhcmFjdGVyKGRhdGVfc3RhcnQpKSwNCiAgICAgICAgICAgICAgIGFscGhhID0gLjgsIGNvbG9yID0gJ2RhcmtyZWQnLCBzaXplID0gMSkgKw0KICAgIGxhYnMoeCA9ICdMb25naXR1ZGUnLCANCiAgICAgICAgIHkgPSAnTGF0aXR1ZGUnLA0KICAgICAgICAgdGl0bGUgPSAnQ29uZmxpY3QgaW4gSW5kaWEgSmFudWFyeSAyMDE5JykNCg0KIyBQbG90bHkgaW5zdGVhZD8NCmluZCA8LSBnZ3Bsb3RseShpbmQsIHdpZHRoID0gNTAwLCBoZWlnaHQgPSA1MDApDQoNCiMgQWRkIG9wdHMNCmluZCAlPiUgDQogICAgYW5pbWF0aW9uX29wdHMoMTAwMCwgZWFzaW5nID0gImxpbmVhciIsIHJlZHJhdyA9IEZBTFNFKSANCg0KDQpgYGANCg0KDQojIyBNb2RlbGluZw0KDQoNCldlIHdpbGwgYnVpbGQgYSByYW5kb20gZm9yZXN0IG1vZGVsIHRvIHByZWRpY3Qgd2hldGhlciBvciBub3QgYSBjb25mbGljdCBoYWQgDQpjaXZpbGlhbiBkZWF0aHMuIA0KDQoNClRoZW4gd2Ugd2lsbCB1c2UgdGhlIG1vZGVsIHRvIGdhdWdlIGlmICd1bmtub3duJyBkZWF0aHMgd2VyZSBwb3NzaWJseSBtaXNsYWJlbGVkLiANCkluIG9yZGVyIHRvIGRvIHRoaXMsIHdlJ3JlIGdvaW5nIHRvIHJlbW92ZSB1bmtub3duIGRlYXRocyBmcm9tIHRoZSB0ZXN0L3RyYWluIHNldC4gDQpUaGVuIGNoZWNrIGlmIHRoZSAndW5rbm93biBkZWF0aHMnIGRhdGEgc2V0IGhhcyBhcm91bmQgdGhlIHNhbWUgYWNjdXJhY3kgYXMgdGhlIA0KdGVzdCBkYXRhIHNldC4gDQoNCg0KQSBkaXNjb3ZlcnkgZnJvbSBvdXIgZXhwbG9yYXRpb24gdGhhdCBjb3VsZCBhZmZlY3QgdXMgcHJlZGljdGluZyBjaXZpbGlhbg0KZGVhdGhzLCBpcyB0aGF0IG9jY2FzaW9uYWxseSBTaWRlIEIgaXMgbGFiZWxlZCBhcyBjaXZpbGlhbnMuIEluIG9yZGVyIHRvIGtlZXAgdGhlIG1vZGVsDQpmcm9tIHVzaW5nIHNpZGVfYiAnQ2l2aWxpYW4nIGxhYmVsaW5nLCB3ZSB3aWxsIG5vdCB1c2UgJ3NpZGVfYicuIEZvciB0aGUgc2FtZQ0KcmVhc29uaW5nLCB3ZSB3aWxsIG5vdCB1c2UgJ3R5cGUgb2YgdmlvbGVuY2UnIGFzIHRoZSB0aGlyZCB0eXBlLCAnT25lIFNpZGVkJyBtZWFucw0KYWN0aW9uIGFnYWluc3QgY2l2bGlhbnMuDQoNCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCg0KIyBTdGVwIG9uZSwgZmlsdGVyIG91dCBVbmtub3ducywgc2V0IHVwIGRhdGEgZnJhbWUuIEJlY3Vhc2Ugb2YgYSBwcmVkaWN0aW9uIHdlIHdpbGwNCiMgYmUgYXR0ZW1wdGluZyBsYXRlciwgd2Ugd2lsbCByZW1vdmUgQnJhemlsIGFuZCBNZXhpY28uIA0Kbm9fdW5rX2RmIDwtIGNfZGYgJT4lIA0KICAgIGZpbHRlcihkZWF0aHNfdW5rbm93biA9PSAwKSAlPiUNCiAgICBmaWx0ZXIoY291bnRyeSAhPSAnTWV4aWNvJywgY291bnRyeSAhPSAnQnJhemlsJykgJT4lIA0KICAgIG11dGF0ZShkYXRlID0gYXMuRGF0ZShkYXRlX3N0YXJ0KSkgJT4lIA0KICAgIHNlbGVjdChpZCwgY29uZmxpY3RfbmV3X2lkLCBkYXRlLCBzaWRlX2EsDQogICAgICAgICAgIG51bWJlcl9vZl9zb3VyY2VzLCBsYXRpdHVkZSwgbG9uZ2l0dWRlLCBjb3VudHJ5LCByZWdpb24sIA0KICAgIGV2ZW50X2NsYXJpdHksIGRlYXRoc19hLCBkZWF0aHNfYiwgY2l2X2NhdCkgJT4lIA0KICAgIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGZhY3RvcikgJT4lIA0KICAgIG11dGF0ZShjb25mbGljdF9uZXdfaWQgPSBmYWN0b3IoY29uZmxpY3RfbmV3X2lkKSkNCg0KDQpgYGANCg0KDQpTcGxpdCBkYXRhIGludG8gdGVzdCBhbmQgdHJhaW5pbmcuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KDQojIENyZWF0ZSB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQpzZXQuc2VlZCg3MTUpDQpjX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQobm9fdW5rX2RmLCBzdHJhdGEgPSBjaXZfY2F0KQ0KY190cmFpbiA8LSB0cmFpbmluZyhjX3NwbGl0KQ0KY190ZXN0IDwtIHRlc3RpbmcoY19zcGxpdCkNCg0KYGBgDQoNClByZXBhcmUgb3VyIGRhdGEuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KDQojIHNldCB1cCBvdXIgcmVjaXBlIHVzaW5nIHRyYWluaW5nIGRhdGENCmNfcmVjIDwtIHJlY2lwZShjaXZfY2F0IH4gLiwgZGF0YSA9IGNfdHJhaW4pICU+JSANCiAgICAjIE1ha2UgSUQgYmUgYW4gaWQNCiAgICB1cGRhdGVfcm9sZShpZCwgbmV3X3JvbGUgPSAnaWQnKSAlPiUgDQogICAgIyBSZWR1Y2UgY2F0ZWdvcmllcw0KICAgIHN0ZXBfb3RoZXIoc2lkZV9hLCBjb3VudHJ5LCBjb25mbGljdF9uZXdfaWQsIHRocmVzaG9sZCA9IC4wMikgJT4lIA0KICAgICMgdHVybiBhbGwgY2F0ZWdvcmllcyBpbnRvIG51bWJlcnMNCiAgICBzdGVwX2R1bW15KGFsbF9ub21pbmFsKCksIC1hbGxfb3V0Y29tZXMoKSkgJT4lIA0KICAgICMgVXNlIHdlZWsNCiAgICBzdGVwX2RhdGUoZGF0ZSwgZmVhdHVyZXMgPSAnd2VlaycpICU+JSANCiAgICAjIFJtIGNvbXBsZXRlIGRhdGUNCiAgICBzdGVwX3JtKGRhdGUpICU+JSANCiAgICAjIGRvd25zYW1wbGUgdGhlIGNpdmlsaWFuIGRlYXRocw0KICAgIHRoZW1pczo6c3RlcF9kb3duc2FtcGxlKGNpdl9jYXQpDQoNCiMgcHJlcCBvdXIgZGF0YQ0KY19wcmVwIDwtIHByZXAoY19yZWMpDQoNCiMgVGFrZSBhIGxvb2sgYXQgbmV3IGRhdGENCmtuaXRyOjprYWJsZShqdWljZShjX3ByZXApICU+JSBzYW1wbGVfbig1KSAlPiUgc2VsZWN0KDE6MTApLCBhbGlnbiA9ICdjJywgY2FwdGlvbiA9ICdQcmVwcGVkIGZvciBNb2RlbGluZywgQSBGZXcgQ29sdW1ucycpDQoNCmBgYA0KDQoNClByZXBhcmUgb3VyIHR1bmluZyBzcGVjaWZpY2F0aW9uLiBJbiB0aGlzIGNhc2Ugd2Ugd2lsbCB0dW5lIHRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyB0byBzYW1wbGUgYXQgDQplYWNoIHNwbGl0LCBhcyB3ZWxsIGFzIHRoZSBudW1iZXIgb2YgZGF0YSBwb2ludHMgcmVxdWlyZWQgdG8gYmUgaW4gYSBub2RlIHRvIGJlIHNwbGl0IGZ1cnRoZXIuDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCiMgU2V0IG91ciByYW5kb20gZm9yZXN0IG1vZGVsIHNwZWNpZmljYXRpb24NCnR1bmVfc3BlYyA8LSByYW5kX2ZvcmVzdCgNCiAgICBtdHJ5ID0gdHVuZSgpLA0KICAgIHRyZWVzID0gMTAwMCwNCiAgICBtaW5fbiA9IHR1bmUoKQ0KICAgICkgJT4lIA0KICAgIHNldF9tb2RlKCdjbGFzc2lmaWNhdGlvbicpICU+JSANCiAgICBzZXRfZW5naW5lKCdyYW5nZXInKQ0KDQoNCmBgYA0KDQoNCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCg0KIyBTZXQgb3VyIHdvcmtmbG93DQp0dW5lX3dmIDwtIHdvcmtmbG93KCkgJT4lIA0KICAgIGFkZF9yZWNpcGUoY19yZWMpICU+JSANCiAgICBhZGRfbW9kZWwodHVuZV9zcGVjKQ0KDQpgYGANCg0KDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCiMgVHJhaW4gb24gb3VyIENWDQpzdGFydC50aW1lIDwtIFN5cy50aW1lKCkNCg0Kc2V0LnNlZWQoMjM0KQ0KDQpjX2ZvbGRzIDwtIHZmb2xkX2N2KGNfdHJhaW4pDQoNCmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbCgpDQoNCiMgVGhpcyB0YWtlcyB0aW1lDQp0dW5lX3JlcyA8LSB0dW5lX2dyaWQoDQogICAgdHVuZV93ZiwgDQogICAgcmVzYW1wbGVzID0gY19mb2xkcywNCiAgICBncmlkID0gMjANCikNCg0KIyBGb3IgdGltaW5nDQplbmQudGltZSA8LSBTeXMudGltZSgpDQp0aW1lLnRha2VuIDwtIGVuZC50aW1lIC0gc3RhcnQudGltZQ0Ka25pdHI6OmthYmxlKHJvdW5kKHRpbWUudGFrZW4pLCBjb2wubmFtZXMgPSAnVGltZSB0byBGaXQnKQ0KDQpgYGANCg0KDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCiMgUGxvdCBvZiBDViBUdW5lZCBIeXBlcnBhcmFtZXRlcnMNCnR1bmVfcmVzICU+JSANCiAgICBjb2xsZWN0X21ldHJpY3MoKSAlPiUgDQogICAgZmlsdGVyKC5tZXRyaWMgPT0gJ3JvY19hdWMnKSAlPiUgDQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCdtaW5fbicsICdtdHJ5JyksIG5hbWVzX3RvID0gJ3R1bmVkJywNCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gJ3R1bmVfdmFsJykgJT4lDQogICAgZ2dwbG90KGFlcyh0dW5lX3ZhbCwgbWVhbiwgY29sb3IgPSB0dW5lZCkpICsNCiAgICBnZW9tX2xpbmUoYWxwaGEgPSAuOCwgc2l6ZSA9IDEuNSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxMCw0MCwyKSkgKw0KICAgIGZhY2V0X3dyYXAofnR1bmVkKSArDQogICAgbGFicyh4ID0gJ1ZhbHVlIG9mIFR1bmVkIEh5cGVycGFyYW1ldGVyJywgeSA9ICdBdmcgUm9jIEF1YycpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpDQoNCmBgYA0KDQoNClRoZSBwbG90IGFib3ZlIHNob3dzIG91ciBSb2MgQXJlYSBVbmRlciB0aGUgQ3VydmUgYXQgZGlmZmVyZW50IGh5cGVycGFyYW1ldGVycw0KbGV2ZWxzLiANCg0KDQpXZSBwaWNrIHRoZSBiZXN0IGh5cGVycGFyYW1ldGVycyBmcm9tIHRoZSB0dW5pbmcgYWJvdmUgYW5kIGZpdCBvdXIgZmluYWwgbW9kZWwuIA0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KDQojIFNlbGVjdCBCZXN0IFNwZWNpZmljYXRpb25zIEJhc2VkIG9uIFJvYyBBdWMNCmJlc3RfYXVjIDwtIHNlbGVjdF9iZXN0KHR1bmVfcmVzLCAncm9jX2F1YycpDQoNCiMgRmluYWxpemUgbW9kZWwgdy90dW5lZCBIeXBlcnBhcm1ldGVycyANCmZpbmFsX3JmIDwtIGZpbmFsaXplX21vZGVsKA0KICAgIHR1bmVfc3BlYywgDQogICAgYmVzdF9hdWMNCikNCg0KYGBgDQoNCg0KQ2hlY2sgdGhlIGltcG9ydGFuY2Ugb2Ygb3VyIHZhcmlhYmxlcy4gDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCiMgVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90cw0KbGlicmFyeSh2aXApDQoNCiMgVklQIFBsb3QNCmZpbmFsX3JmICU+JSANCiAgICBzZXRfZW5naW5lKCdyYW5nZXInLCBpbXBvcnRhbmNlID0gJ3Blcm11dGF0aW9uJykgJT4lIA0KICAgIGZpdChjaXZfY2F0fiAuLA0KICAgICAgICBkYXRhID0ganVpY2UoY19wcmVwKSAlPiUgc2VsZWN0KC1pZCkpICU+JSANCiAgICB2aXAoZ2VvbSA9ICdwb2ludCcpICsNCiAgICBsYWJzKHRpdGxlID0gJ1ZhcmlhYmxlIEltcG9ydGFuY2UnKQ0KDQoNCmBgYA0KDQoNCg0KVGhlIGRlYXRocyBhcmUgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlcyBmb3IgcHJlZGljdGluZyBjaXZpbGlhbiBjYXN1YWx0aWVzLiANClNwZWNpZmljYWxseSB0aGUgZGVhdGhzIG9mICdzaWRlX2InLiBJdCdzIGhhcmQgdG8gc2F5IGV4YWN0bHkgd2h5LiBJcyBpdCBiZWNhdXNlDQp3aXRoIGhpZ2hlciBkZWF0aHMgZnJvbSBzaWRlIEIgdGhlcmUncyBtb3JlIGxpa2VseSB0byBiZSBjaXZpbGlhbiBjYXN1YWx0aWVzPyBPcg0KaXMgaXQgYmVjYXVzZSBjaXZpbGlhbiBkZWF0aHMgY2FuIGJlIGx1bXBlZCBpbiB3aXRoIHNpZGVfYiwgbGVhZGluZyB0byAnbm8nIGNpdmlsaWFuIGRlYXRocyBidXQgaGlnaCBzaWRlX2IgZGVhdGhzPyBUaGlzIGlzLCBvZiBjb3Vyc2UsIGNvbXBsZXRlIGFuZCB1bmlmb3JtZWQgY29uamVjdHVyZS4gDQoNCg0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KDQojIEZpbmFsIHdvcmtmbG93LCB3aXRoIHJlY2lwZSBhbmQgZmluYWwgbW9kZWwuIA0KZmluYWxfd2YgPC0gd29ya2Zsb3coKSAlPiUgDQogICAgYWRkX3JlY2lwZShjX3JlYykgJT4lIA0KICAgIGFkZF9tb2RlbChmaW5hbF9yZikNCg0KYGBgDQoNCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCiMgbGFzdF9maXQgZG9lcyBhIGZpbmFsIGZpdCBvbiB0cmFpbmluZyBkYXRhLCB0aGVuIGV2YWx1YXRlcyBvbiB0ZXN0aW5nIGRhdGENCmZpbmFsX3JlcyA8LSBmaW5hbF93ZiAlPiUgDQogICAgbGFzdF9maXQoY19zcGxpdCkNCg0KIyBTYXZlIHRlc3RpbmcgYWNjdXJhY3kgZm9yIGxhdGVyDQp0ZXN0aW5nX2FjY3VyYWN5IDwtIGZpbmFsX3JlcyAlPiUgDQogICAgY29sbGVjdF9tZXRyaWNzKCkgJT4lIA0KICAgIGZpbHRlcigubWV0cmljID09ICdhY2N1cmFjeScpICU+JSANCiAgICBzZWxlY3QoLmVzdGltYXRlKQ0KDQojIExvb2sgYXQgb3VyIG1ldHJpY3MNCmtuaXRyOjprYWJsZShmaW5hbF9yZXMgJT4lIA0KICAgIGNvbGxlY3RfbWV0cmljcygpICU+JSBtdXRhdGUoLmVzdGltYXRlID0gcm91bmQoLmVzdGltYXRlLCAzKSksIGFsaWduID0nYycsIGNhcHRpb24gPSAnTWV0cmljcycpDQoNCmBgYA0KDQoNCk5vdCBiYWQuIA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCiMgR2xhbmNlIGF0IHByZWRpY3Rpb25zDQprbml0cjo6a2FibGUoZmluYWxfcmVzICU+JSANCiAgICBjb2xsZWN0X3ByZWRpY3Rpb25zKCkgJT4lIHNhbXBsZV9uKDUpLCBhbGlnbiA9J2MnLCBjYXB0aW9uID0gJ1ByZWRpY3Rpb25zJykNCg0KYGBgDQoNCldlbGwgdGhhdCBidWlsdCB1cyBhIG1vZGVsLiBOb3cgY2FuIHdlICB1c2UgaXQgb24gdGhlICd1bmtub3duJyBkZWF0aHMgdG8gcHJlZGljdA0Kd2hpY2ggb25lcyBtaWdodCBoYXZlIGhhZCBjaXZpbGlhbiBjYXN1YWx0aWVzPw0KDQoNCkkgd29uZGVyIGlmIHRoZSBldmVudHMgd2l0aCB0aGUgdW5rbm93bnMgYXJlIGZyb20gdGhlIHNhbWUgZGlzdHJpYnV0aW9uLiANCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCg0KIyBDcmVhdGUgZ3JvdXBzIGZvciB3aGVuIHRoZXJlJ3MgdW5rbm93biBkZWF0aHMsIHRoZW4gcGxvdC4gDQpjX2RmICU+JSANCiAgICBtdXRhdGUodW5fY2F0ID0gY2FzZV93aGVuKGRlYXRoc191bmtub3duID4gMCB+ICdZZXMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdObycpKSAlPiUgDQogICAgZmlsdGVyKGJlc3QgPCA3NSkgJT4lIA0KICAgIGdncGxvdChhZXMoYmVzdCwgZmlsbCA9IHVuX2NhdCkpICsNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAuNSkgKw0KICAgIGxhYnMoeCA9ICdFc3RpbWF0ZWQgRGVhdGhzJywgeSA9ICdOdW1iZXIgb2YgRXZlbnRzJywNCiAgICAgICAgIHRpdGxlID0gJ0V2ZW50cyBXaXRoIFVua25vd24gRGVhdGhzJykgKw0KICAgIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICdVbmtvd24gRGVhdGhzJykNCg0KDQpgYGANCg0KDQpGcm9tIHRoaXMgZmlyc3QgcXVpY2sgZ2xhbmNlLCB0aGV5IGFwcGVhciBwcmV0dHkgY2xvc2UuIA0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KZXh0cmFmb250Ojpsb2FkZm9udHMoZGV2aWNlPSJ3aW4iKQ0KDQojIEZpbHRlcmluZyBieSBldmVudHMgZ3JlYXRlciB0aGFuIDc1LCBhbmQgZmluZGluZyBwcm9wb3J0aW9ucyBvZiBldmVudHMNCiMgd2l0aCB1bmtub3duIGRlYXRocy4gDQpjX2RmICU+JSANCiAgICBtdXRhdGUodW5fY2F0ID0gY2FzZV93aGVuKGRlYXRoc191bmtub3duID4gMCB+ICdZZXMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdObycpKSAlPiUgDQogICAgY291bnQoY291bnRyeSwgdW5fY2F0KSAlPiUgDQogICAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIA0KICAgIG11dGF0ZShwcm9wb3J0aW9uID0gKG4gLyBzdW0obikpKSAlPiUgDQogICAgZmlsdGVyKG4gPiA3NSkgJT4lIA0KICAgIGdncGxvdChhZXMoY291bnRyeSwgcHJvcG9ydGlvbiwgZmlsbCA9IHVuX2NhdCkpICsNCiAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICdzdGFjaycpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoJ1Vua293biBEZWF0aHMnKSwgDQogICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwKSkgKw0KICAgIGxhYnModGl0bGUgPSAnUHJvcG9ydGlvbiBvZiBFdmVudHMgd2l0aCBVbmtub3duIERlYXRocyB2cyBXaXRob3V0JykgKw0KICAgIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICdVbmtub3duIERlYXRocycpDQoNCmBgYA0KDQpNZXhpY28sIG9uZSBvZiB0aGUgY291bnRpZXMgd2l0aCB0aGUgbW9zdCBldmVudHMsIGhhcyBhIA0KdmVyeSBsYXJnZSBwcm9wb3J0aW9uIG9mIHVua25vd24gZGVhdGhzLiBCcmF6aWwgYXMgd2VsbC4gVGhpcyBsZWFkcyBtZSB0byBjb25jbHVkZQ0KdGhhdCBpdCBpcyBOT1QgYXBwcm9wcmlhdGUgdG8gdXNlIG15IG1vZGVsIHRvIHByZWRpY3QgY2l2aWxpYW4gY2FzdWFsdGllcyBvbiANCnVua25vd24gZGVhdGhzIGluIE1leGljbyBvciBCcmF6aWwuIFRoZXJlZm9yZSwgd2Ugd2lsbCByZXRyYWluIG91ciBtb2RlbCBleGNsdWRpbmcNCnRob3NlIHR3byBjb3VudHJpZXMuIFRoZW4gcHJlZGljdCB1bmtub3duIGRlYXRocyB2cyBjaXZpbGlhbiBkZWF0aHMgb24gdGhlIHJlc3QuDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCiMgR2V0IGFsbCBkYXRhIHdlIHB1bGxlZCBmcm9tIGVhcmxpZXINCnVuayA8LSBjX2RmICU+JSANCiAgICBmaWx0ZXIoZGVhdGhzX3Vua25vd24gPiAwKSAlPiUgDQogICAgZmlsdGVyKGNvdW50cnkgIT0gJ01leGljbycsIGNvdW50cnkgIT0gJ0JyYXppbCcpICU+JSANCiAgICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZV9zdGFydCkpICU+JSANCiAgICBzZWxlY3QoaWQsIGNvbmZsaWN0X25ld19pZCwgZGF0ZSwgc2lkZV9hLCANCiAgICBudW1iZXJfb2Zfc291cmNlcywgbGF0aXR1ZGUsIGxvbmdpdHVkZSwgY291bnRyeSwgcmVnaW9uLCANCiAgICBldmVudF9jbGFyaXR5LCBkZWF0aHNfYSwgZGVhdGhzX2IsIGNpdl9jYXQpICU+JSANCiAgICBtdXRhdGVfaWYoaXMuY2hhcmFjdGVyLCBmYWN0b3IpICU+JSANCiAgICBtdXRhdGUoY29uZmxpY3RfbmV3X2lkID0gZmFjdG9yKGNvbmZsaWN0X25ld19pZCkpDQoNCg0KYGBgDQoNCg0KT3VyIHByZWRpY3RhYmxlIHVua25vd24gZGF0YSBpcyByZWR1Y2VkIGZyb20gMTU1MCB0byA2MTEgd2hlbiB3ZSByZW1vdmUgQnJhemlsIGFuZCBNZXhpY28uIEFnYWluLCBhcyBhIHJlbWluZGVyLCB3ZSdyZSBhc3N1bWluZyB0aGVyZSdzIG5vIHN1Y2ggJ3JlYWwnIGNhdGVnb3J5IG9mIA0KdW5rbm93biBjYXN1YWx0aWVzOiBjYXN1YWx0aWVzIGVpdGhlciBjb21lIGZyb20gY2l2aWxpYW5zIG9yIGNvbWJhdGFudHMuIFNvLA0Kd2UncmUgdXNpbmcgb3VyIG1vZGVsIHRvIHByZWRpY3QgY2l2aWxpYW4gY2FzdWFsdGllcyBvbiBldmVudHMgd2l0aCB1bmtub3duIGRlYXRocy4gDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQojIEZpbmFsIGZpdCB1c2VkIG9uIHRyYWluaW5nIGRhdGENCmNvbXBsZXRlZCA8LSBmaW5hbF93ZiAlPiUgDQogICAgZml0KGNfdHJhaW4pDQoNCiMgVHJhbnNmb3JtIG91ciBuZXcgJ3Vua25vd25zJyBkYXRhIGludG8gc2FtZSBhcyBtb2RlbA0KdW5rX2Jha2VkIDwtIGJha2UocHJlcChjX3JlYyksIHVuaykNCg0KIyBQdWxsIG91dCBtb2RlbA0KbXlfbW9kZWwgPC0gY29tcGxldGVkICU+JSANCiAgICBwdWxsX3dvcmtmbG93X2ZpdCgpDQoNCiMgTWFrZSBwcmVkaWN0aW9ucw0KcHJlZGljdGVkX3Vua25vd25zIDwtIHByZWRpY3QobXlfbW9kZWwsIG5ld19kYXRhID0gdW5rX2Jha2VkKSAlPiUgDQogICAgY2JpbmQodW5rLCAuKQ0KDQojIEdldCBjb2x1bW4gb2YgdW5rbm93biBkZWF0aHMNCnVua19kZWF0aHMgPC0gc2VsZWN0KGNfZGYsIGlkLCBkZWF0aHNfdW5rbm93bikNCg0KIyBBZGQgb3VyIFVua25vd24gZGVhdGhzIGJhY2ssIGFuZCBrZWVwIG9ubHkgcmVsZXZhbnQgZGF0YS4gDQpwcmVkX3Nob3J0IDwtIHByZWRpY3RlZF91bmtub3ducyAlPiUgDQogICAgbGVmdF9qb2luKHggPSAuLCB5ID0gdW5rX2RlYXRocywgYnkgPSAnaWQnKSAlPiUgDQogICAgc2VsZWN0KGlkLCBjb3VudHJ5LCBkZWF0aHNfdW5rbm93biwgY2l2X2NhdCwgLnByZWRfY2xhc3MpDQoNCmtuaXRyOjprYWJsZShwcmVkX3Nob3J0ICU+JSBzYW1wbGVfbig4KSwgYWxpZ24gPSAnYycsIGNhcHRpb24gPSAnVW5rbm93biBEZWF0aHMsIENpdmlsaWFuIERlYXRocyBhbmQgQ2l2IERlYXRoIFByZWRpY3Rpb25zJykNCmBgYA0KDQpUaGVyZSBhcHBlYXJzIHRvIGJlIHNvbWUgbWlzbWF0Y2ggYmV0d2VlbiB0aGUgY2l2aWxpYW4gZGVhdGhzIGNhdGVnb3J5IGFuZCBvdXIgcHJlZGljdGlvbnMuIA0KQXNzdW1pbmcgb3VyIG1vZGVsIHJldGFpbnMgaXQncyBhY2N1cmFjeSBvbiBvdXIgJ25ldycgZGF0YSwgd2UgcG9zdHVsYXRlIHRoYXQNCnRoZXJlIHNob3VsZCBiZSBhYm91dCBhIDkwJSBhY2N1cmFjeSByYXRlLiANCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQojIEdldCBvdXIgYWNjdXJhY3kgb24gb3VyIG5ldyBkYXRhDQp1bmtub3duX2FjY3VyYWN5IDwtIHN1bShwcmVkX3Nob3J0JGNpdl9jYXQgPT0gcHJlZF9zaG9ydCQucHJlZF9jbGFzcykgLyBucm93KHByZWRfc2hvcnQpDQoNCiMgQ3JlYXRlIGRhdGFmcmFtZSBmb3IgcGxvdA0KY29tcGFyZWRfcGVyYyA8LSBkYXRhLmZyYW1lKGFjYyA9IGMoJ1Vua25vd24gQWNjdXJhY3knLCAnVGVzdGluZyBBY2N1cmFjeScpLCANCiAgICAgICAgICAgICAgICAgIHBlcmMgPSBjKHNjYWxlczo6cGVyY2VudCh1bmtub3duX2FjY3VyYWN5KSwgc2NhbGVzOjpwZXJjZW50KHB1bGwodGVzdGluZ19hY2N1cmFjeSkpKSkNCg0KIyBObyBwZXJjDQpjb21wYXJlZCA8LSBkYXRhLmZyYW1lKGFjYyA9IGMoJ1Vua25vd24gQWNjdXJhY3knLCAnVGVzdGluZyBBY2N1cmFjeScpLCANCiAgICAgICAgICAgICAgICAgIHBlcmMgPSBjKHVua25vd25fYWNjdXJhY3ksIHB1bGwodGVzdGluZ19hY2N1cmFjeSkpKQ0KDQojIFBsb3QNCmdncGxvdChjb21wYXJlZCkgKw0KICAgIGdlb21fY29sKGFlcyh4ID0gYWNjLCB5ID0gcGVyYywgZmlsbCA9IGFjYykpICsNCiAgICBsYWJzKHggPSAnJywgeSA9ICdQZXJjZW50IEFjY3VyYXRlJywgdGl0bGUgPSAnQWNjdXJhY3kgb2YgT3VyIFR3byBTZXRzJywNCiAgICAgICAgIHN1YnRpdGxlID0gJ01vZGVsIFRlc3RpbmcgU2V0IHZzLiBVbmtub3duIERlYXRocyBTZXQnKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSksIGxhYmVscyA9IHBlcmNlbnQpDQoNCmBgYA0KDQpUaGlzIHRlbGxzIG1lIHR3byB0aGluZ3MsIG9mIHdoaWNoIGVpdGhlciwgb3IgYm90aCwgY291bGQgYmUgdHJ1ZS4NCg0KT25lLCB1c2luZyB0aGUgbW9kZWwgb24gdGhlIHVua25vd24gc3Vic2V0IG1hZGUgZm9yIGhpZ2hseSBpbmFjY3VyYXRlIHByZWRpY3Rpb25zDQpvbiB0aGUgY2l2aWxpYW4gZGVhdGhzIGNhdGVnb3J5LiANCg0KT3IgdHdvLCB0aGUgJ3Vua25vd24nIGRlYXRocyBoYXZlIGFuIHVudXN1YWxseSBoaWdoIGluYWNjdXJhdGUgbGFiZWxpbmcgb2YgDQpjaXZpbGlhbiBjYXN1YWx0aWVzLiANCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmtuaXRyOjprYWJsZShjb21wYXJlZF9wZXJjLCBhbGlnaHQgPSAnYycsIGNhcHRpb24gPSAnQWNjdXJhY3kgb2YgTW9kZWwgb24gVW5rbm93bnMgdnMgVGVzdGluZyBEYXRhJykNCmBgYA0KDQoNCioqVGhhbmsgeW91LCBhbmQgZmVlbCBmcmVlIHRvIGNvbnRhY3QgbWUgdGhyb3VnaCBnaXRodWIgd2l0aCBhbnkgcXVlc3Rpb25zLCBlcnJvcnMsKioNCioqb3IgY29tbWVudHMuKioNCg0KLUNhcnNvbiBQb2UNCg0KDQoqVGhpcyBwcm9qZWN0IHdhcyBoZWF2aWx5IGluc3BpcmVkIGJ5IFtEYXZpZCBSb2JpbnNvbl0oaHR0cDovL3ZhcmlhbmNlZXhwbGFpbmVkLm9yZy9hYm91dC8pKiANCiphbmQgW0p1bGlhIFNpbGdlXShodHRwczovL2p1bGlhc2lsZ2UuY29tL2Fib3V0LykuIFRoYW5rIHlvdSBmb3IgdGhlIGluc3BpcmF0aW9uYWwqDQoqdHV0b3JpYWxzISo=